/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2015 d3leaf@126.com
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
package com.mapperdb.helper;

import java.io.FileInputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.ibatis.annotations.Result;
import org.apache.ibatis.annotations.SelectKey;
import org.apache.ibatis.parsing.XNode;
import org.apache.ibatis.parsing.XPathParser;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.TypeHandler;
import org.apache.ibatis.type.UnknownTypeHandler;

import com.mapperdb.db.ResultEntity;
import com.mapperdb.util.MapperUtils;

public class ResultHelper {
	private static final Map<Class<?>, ResultEntity> resultEntityMap = new HashMap<Class<?>, ResultEntity>();

	public static Map<Class<?>, ResultEntity> getResultEntityMap() {
		return resultEntityMap;
	}

	public static ResultEntity buildResultEntity(String tableName, Class<?> entityClass, SelectKey selectKey, Result[] results) {
		ResultEntity resultEntity = resultEntityMap.get(entityClass);
		if (resultEntity == null) {
			synchronized (resultEntityMap) {
				if (resultEntity == null) {
					ResultEntity.Builder builder = new ResultEntity.Builder();
					builder.tableName(tableName);
					builder.entityClass(entityClass);
					builder.selectKey(selectKey);
					builder.results(results);
					resultEntityMap.put(entityClass, resultEntity = builder.build());
				}
			}
		}
		return resultEntity;
	}

	public static ResultEntity getResultEntity(Class<?> entityClass) {
		ResultEntity resultEntity = resultEntityMap.get(entityClass);
		if (resultEntity == null) {
			Result[] results = MapperUtils.getResults(entityClass);
			String tableName = MapperUtils.getTableName(entityClass);
			SelectKey selectKey = MapperUtils.parseSelectKey(entityClass);
			return buildResultEntity(tableName, entityClass, selectKey, results);
		}
		return resultEntity;
	}

	public static ResultEntity getResultEntity(String id) {
		return getResultEntity(MapperUtils.getGenericType(id));
	}

	public static String formatPropertyEqColumn(Result result) {
		return formatPropertyEqColumn(result, result.property());
	}

	public static String formatPropertyEqColumn(Result result, String property) {
		StringBuffer sb = new StringBuffer();
		sb.append(result.column()).append(" = ");
		sb.append(formatProperty(result, property));
		return sb.toString();
	}

	public static String format(Result result) {
		return formatProperty(result, result.property());
	}

	public static String formatProperty(Result result, String property) {
		Class<?> javaType = result.javaType();
		JdbcType jdbcType = result.jdbcType();
		Class<? extends TypeHandler<?>> typeHandler = result.typeHandler();

		StringBuffer sb = new StringBuffer();
		sb.append("#{").append(property);
		if (javaType != null && javaType != Void.TYPE) sb.append(",javaType=").append(javaType.getName());
		if (jdbcType != null && jdbcType != JdbcType.UNDEFINED) sb.append(",jdbcType=").append(jdbcType.name());
		if (typeHandler != null && typeHandler != UnknownTypeHandler.class)
			sb.append(",typeHandler=").append(typeHandler.getName());
		sb.append("}");
		return sb.toString();
	}

	public static String parseResults(InputStream inputStream, int index) {
		ResultParser parser = new ResultParser(inputStream);
		parser.parser();
		return parser.resultsList.get(index);
	}

	public static String parseResults(InputStream inputStream) {
		ResultParser parser = new ResultParser(inputStream);
		parser.parser();
		return parser.resultsList.get(0);
	}

	public static class ResultParser {
		XPathParser parser;
		List<String> resultsList = new ArrayList<String>();

		public ResultParser(InputStream inputStream) {
			this.parser = new XPathParser(inputStream);
		}

		public void parser() {
			configurationElement(parser.evalNode("/mapper"));
		}

		protected void configurationElement(XNode context) {
			resultMapElements(context.evalNodes("/mapper/resultMap"));
		}

		protected void resultMapElements(List<XNode> evalNodes) {
			for (XNode xNode : evalNodes) {
				resultsList.add(resultMapElement(xNode));
			}
		}

		protected String resultMapElement(XNode resultMapNode) {
			List<XNode> resultChildren = resultMapNode.getChildren();
			StringBuffer sb = new StringBuffer("@Results({\n");
			boolean isFrist = true;
			for (XNode resultChild : resultChildren) {
				sb.append("\t");
				if (!isFrist) sb.append(",");
				isFrist = false;
				boolean id = "id".equals(resultChild.getName());

				sb.append(buildResultMappingFromContext(resultChild, id));
			}
			sb.append("})");
			return sb.toString();
		}

		protected String buildResultMappingFromContext(XNode context, boolean id) {
			String property = context.getStringAttribute("property");
			String column = context.getStringAttribute("column");
			String javaType = context.getStringAttribute("javaType");
			String jdbcType = context.getStringAttribute("jdbcType");
			String typeHandler = context.getStringAttribute("typeHandler");
			StringBuffer sb = new StringBuffer();
			sb.append("@Result(");
			sb.append("id = ").append(id);
			sb.append(" , ").append("column = \"").append(column).append("\"");
			sb.append(" , ").append("property = \"").append(property).append("\"");
			sb.append(" , ").append("jdbcType = JdbcType.").append(jdbcType);
			if (javaType != null) sb.append(" , ").append("javaType = ").append(javaType);
			if (typeHandler != null) sb.append(" , ").append("typeHandler = ").append(typeHandler);
			sb.append(")\n");
			return sb.toString();
		}
	}

	public static void main(String[] args) throws Exception {
		String path = "D:/work/sts-3.6.4.RELEASE/workspace/mapperdb/src/test/java/com/demo/mapping/BlogMapper.xml";
		InputStream inputStream = new FileInputStream(path);
		String results = parseResults(inputStream);
		System.out.println(results);
	}
}
